define(function(require, exports, module) {
    'use strict';

    var rk_userService = require('core/services/2.0/rk_userService');

    var scheduleUtil = require('oa/js/schedule/asset/utils/scheduleUtil.sort');

    (function() {
        var NUM_TRUE = '1';
        var NUM_FALSE = '0';
        //frequency: DAY = 1, WEEK = 2, MONTH=3
        var FRQ_BY_DAY = '1';
        var FRQ_BY_WEEK = '2';
        var FRQ_BY_MONTH = '3';
        //recurStopCondition: NAVER = 1, DATE = 2, Short TIMES=3
        var STOP_BY_NEVER = '1';
        var STOP_BY_DATE = '2';
        var STOP_BY_STIMES = '3';

        var MS_A_DAY = 1000 /*ms*/ * 60 /*sec*/ * 60 /*min*/ * 24 /*hours*/ ;
        var MS_A_WEEK = MS_A_DAY * 7;


        var g_scheduleList = [];
        var g_taskList = [];

        function getRenderEventsByRules(mi, data) {
            var view = $('#calendar').fullCalendar('getView');
            var interval = mi.interval;
            var events = [];
            //if(view.name == 'agendaDay') return events;
            var start = moment(mi.renderStartMom);
            var end = moment(start).add(mi.valueLength, 'days');
            start.hour(12); //hour=12 is for accurate day diff calculate
            end.hour(12); //hour=12 is for accurate day diff calculate
            var frequency = data.frequency;

            //转为整数
            var datarules = data.rules.split(',');
            var rules = [];
            for (var i = 0, len = datarules.length; i < len; i++) {
                var num = parseInt(datarules[i]);
                if (!isNaN(num)) rules.push(num);
            }

            var startMom = moment(mi.calendarStartMom);
            var endMom = moment(mi.calendarEndMom);

            mi.defFinishMom.hour(12); //注意，在rules日程中，finishMom的一天的0点0分算这一天，因此时间设为12点以防漏掉

            if (mi.valueStartMom.isAfter(startMom)) startMom = moment(mi.valueStartMom);
            if (endMom.isAfter(mi.defFinishMom)) endMom = moment(mi.defFinishMom);

            //startMom.subtract(1, 'day');//多加减一天，确保覆盖
            while (!startMom.isAfter(endMom)) {
                var calEvent = {
                    id: data.id,
                    name: data.name,
                    colorSetting: data.colorType.color,
                    title: mi.data.name,
                    allDay: (data.isAllDay == '1' ? true : false),
                    valueAllDay: (data.isAllDay == '1' ? true : false),
                    valueLength: mi.valueLength,
                    createdBy: data.createdBy,
                    valueStartMom: moment(mi.valueStartMom),
                    valueEndMom: moment(mi.valueEndMom),
                    isRepeat: false,
                    interval: interval,
                    accessible: data.accessible,
                    visitRecords: data.visitRecords,
                    colorType: data.colorType
                };
                if (frequency === FRQ_BY_WEEK) {
                    var day = startMom.weekday() + 1;
                    if (_.includes(rules, day)) {
                        calEvent.start = startMom.format();
                        calEvent.end = startMom.format();
                        events.push(calEvent);
                    }
                }
                if (frequency === FRQ_BY_MONTH) {
                    var day = startMom.get('date');
                    if (_.includes(rules, day)) {
                        calEvent.start = startMom.format();
                        calEvent.end = startMom.format();
                        events.push(calEvent);
                    }
                }
                startMom.add(1, 'day');
            }
            return events;
        };

        function getRenderEventsByDayWeekRepeat(mi, data) {
            var interval = mi.interval;

            var events = [];
            var start = moment(mi.renderStartMom);
            var end = moment(start).add(mi.valueLength, 'days');
            start.hour(12); //hour=12 is for accurate day diff calculate
            end.hour(12); //hour=12 is for accurate day diff calculate
            mi.renderEndMom.add(12, 'hours'); //+12h, if start and end are both added 12h
            //if(data.isAllDay == '1')end.add(12, 'hour');//+12h, for fullcalendar issue, diff() will be lose 1day if allDay=true

            while (end.isBefore(mi.renderEndMom) || end.isSame(mi.renderEndMom)) {
                var calEvent = {
                    id: data.id,
                    name: data.name,
                    colorSetting: data.colorType.color,
                    title: mi.data.name,
                    allDay: (data.isAllDay == '1' ? true : false),
                    valueAllDay: (data.isAllDay == '1' ? true : false),
                    valueLength: mi.valueLength,
                    start: start.format(),
                    end: end.format(),
                    createdBy: data.createdBy,
                    valueStartMom: moment(mi.valueStartMom),
                    valueEndMom: moment(mi.valueEndMom),
                    isRepeat: true,
                    interval: interval,
                    accessible: data.accessible,
                    colorType: data.colorType
                };
                events.push(calEvent);
                start.add(interval, mi.type);
                end = moment(start).add(mi.valueLength, 'days');
                //if(data.isAllDay == '1')end.add(12, 'hours');
                start.hour(12);
                end.hour(12);
                //end.add(12, 'hour')
            }
            return events;
        };

        function getDiffMonths(startYear, startMonth, currentYear, currentMonth) {
            //momentjs.diff('months') is compare day and day, but here should compare month and month
            var m1 = moment({
                y: startYear,
                M: startMonth,
                d: 1,
                h: 12,
                m: 0,
                s: 0,
                ms: 0
            });
            var m2 = moment({
                y: currentYear,
                M: currentMonth,
                d: 1,
                h: 12,
                m: 0,
                s: 0,
                ms: 0
            });
            return m2.diff(m1, 'months');
        };

        function getRenderEventsByMonthRepeat(mi, data) {
            var events = [];
            var interval = mi.interval;

            var calendarMonth = moment(mi.calendarStartMom).months();
            var eventStartDate = moment(mi.valueStartMom).dates();
            var eventLength = mi.valueLength;

            var idayMom = moment(mi.calendarStartMom).hour(12); //hour=12 is for accurate day diff calculate
            idayMom.subtract(eventLength, 'days'); //make sure a very-long prev event(>30days) can be displayed at start view		
            if (idayMom.isBefore(mi.valueStartMom)) idayMom = moment(mi.valueStartMom);

            while (idayMom.isBefore(mi.calendarEndMom) || idayMom.isSame(mi.calendarEndMom)) {
                if (idayMom.isAfter(data.finalFinishTime)) break;
                var date = idayMom.date();
                var show = false;
                if (idayMom.dates() == eventStartDate) {
                    show = true;
                    var diffMonths = getDiffMonths(mi.valueStartMom.years(), mi.valueStartMom.months(), idayMom.years(), idayMom.months());
                    if (diffMonths % interval != 0) show = false; //support interval
                } else {
                    //for Feb. and month without 31, its ok to turn off if you don not want this feature
                    if (false)
                        if (date >= 28 && eventStartDate > 28) {
                            var nextDay = moment(idayMom);
                            nextDay.add(1, 'days').hour(12);
                            if (nextDay.dates() == 1 && (date < eventStartDate)) { //the end of the month
                                show = true;
                                var diffMonths = getDiffMonths(mi.valueStartMom.years(), mi.valueStartMom.months(), idayMom.years(), idayMom.months());
                                if (diffMonths % interval != 0) show = false; //support interval
                            }
                        }
                }
                //console.debug(date);
                if (show) {
                    var calEvent = {
                        id: data.id,
                        name: data.name,
                        colorSetting: data.colorType.color,
                        title: mi.data.name,
                        start: moment(idayMom),
                        end: moment(idayMom).add(eventLength, 'days'),
                        valueLength: mi.valueLength,
                        valueAllDay: (data.isAllDay == '1' ? true : false),
                        createdBy: data.createdBy,
                        valueStartMom: moment(mi.valueStartMom),
                        valueEndMom: moment(mi.valueEndMom),
                        isRepeat: true,
                        interval: interval,
                        accessible: data.accessible,
                        colorType: data.colorType
                    };
                    events.push(calEvent);
                }
                idayMom.add(1, 'days').hour(12);
                //console.debug('    ->'+idayMom.format('YYYY-MM-DD'));
            }

            return events;
        };

        function drawNormalEvent(mi, data) {
            var startMom = $.isNumeric(data.startDate) ? moment(parseInt(data.startDate)) : moment(data.startDate);
            var endMom = $.isNumeric(data.endDate) ? moment(parseInt(data.endDate)) : moment(data.endDate);

            //must be 12:00, for cross day displaying
            startMom.hours(12);
            endMom.hours(12);

            var calEvent = {
                id: data.id,
                name: data.name,
                createdBy: data.createdBy,
                colorSetting: data.colorType.color,
                title: data.name,
                start: startMom,
                end: endMom,
                valueStartMom: moment(mi.valueStartMom),
                valueEndMom: moment(mi.valueEndMom),
                allDay: (data.isAllDay == '1' ? true : false),
                valueAllDay: (data.isAllDay == '1' ? true : false),
                accessible: data.accessible,
                visitRecords: data.visitRecords,
                colorType: data.colorType
            };
            return calEvent;
        }

        function getMomentInfo(calenderSpan, data) {
            var interval = data.interval ? parseInt(data.interval) : 0; //gaps between starting-day of repeating events
            //calendar view
            var calendarStartMom = calenderSpan.start;
            var calendarEndMom = calenderSpan.end;
            //user defined
            // var valueStartMom = $.isNumeric(data.startDate) ? moment(parseInt(data.startDate)) : moment(data.startDate);
            // var valueEndMom = $.isNumeric(data.endDate) ? moment(parseInt(data.endDate)) : moment(data.endDate);
            var valueStartMom = data.startDateMom;
            var valueEndMom = data.endDateMom;
            var valueLength = valueStartMom.isSame(valueEndMom, 'days') ? 0 : valueEndMom.diff(valueStartMom, 'days'); //day length of an event

            var defFinishMom = data.finalFinishTime ? moment(data.finalFinishTime) : moment(calendarEndMom);

            //actrully rendering
            var renderStartMom = calendarStartMom.isBefore(valueStartMom) ? moment(valueStartMom) : getNearbyStartMom(moment(valueStartMom), moment(calendarStartMom), valueLength, interval, data.frequency);
            var renderEndMom = defFinishMom.isBefore(calendarEndMom) ? moment(defFinishMom) : moment(calendarEndMom);

            renderEndMom.add(valueLength, 'days'); //should be greater (+ one valueLength) so the last event could be rendered.
            return {
                valueStartMom: valueStartMom,
                valueEndMom: valueEndMom,
                valueLength: valueLength,
                defFinishMom: defFinishMom,
                calendarStartMom: calendarStartMom,
                calendarEndMom: calendarEndMom,
                renderStartMom: renderStartMom,
                renderEndMom: renderEndMom,
                interval: interval,
                data: data
            }
        };

        function getNearbyStartMom(valueStartMom, calendarStartMom, valueLength, interval, frequency) {
            //****************************************
            //   EXAMPLE: [U^]EOOOEOOOEOOO..[V^]...[V$]
            //     'U^' means user defined start date, 
            //     'V^ -> V$' means current view date of calendar , 
            //     'E' means days of valueLength, 'O' means days of frequence, 'EOOO' means one repeating including event-days and interval-days
            //****************************************
            var diff_days = calendarStartMom.diff(valueStartMom, 'days');
            //DAY or WEEK
            if (frequency == FRQ_BY_DAY || frequency == FRQ_BY_WEEK) {
                var step_distance = interval * (frequency == FRQ_BY_DAY ? 1 /*DAY*/ : 7 /*WEEK*/ );
                if (diff_days > valueLength) {
                    var how_many_steps = Math.floor(diff_days / step_distance);
                    var nearbyStartMom = moment(valueStartMom.add(how_many_steps * step_distance, 'days'));
                    return nearbyStartMom;
                } else {
                    return valueStartMom;
                }
            }
        };

        function updateScheduleTitle(data) { //部分日程的标题需要更新
            if (data.data.schedules && data.data.schedules.length > 0) {
                $.each(data.data.schedules, function(i, schedule) {
                    if (schedule.colorType.dummyType == 32) {
                        schedule.name = "拜访：" + schedule.fromName;
                    }
                });
            }
            return data;
        };

        function bindScheduleEvent(data) {
            if (!data.data || !data.data.schedules) return;
            data = updateScheduleTitle(data);
            var schedules = data.data.schedules;
            if (schedules.length > 0) {
                g_scheduleList = g_scheduleList.concat(schedules);
            }
            var list = filterSchedule(g_scheduleList);
            renderScheduleEvents(list);
        };

        function bindTaskEvent(data) {
            if (!data.data || !data.data.tasks) return;
            var tasks = data.data.tasks;
            if (tasks.length > 0) {
                g_taskList = g_taskList.concat(tasks);
            }
            var list = filterTask(g_taskList);
            renderTaskEvents(list);
        };

        function filterSchedule(list) {
            return list;
        }

        function filterTask(oldList) {
            return oldList;
            var showTask = scheduleUtil.getDisplaySetting('showTask');
            if (showTask == false) return [];

            var onlyShowUnfinishedTasks = scheduleUtil.getDisplaySetting('onlyShowUnfinishedTasks');
            if (onlyShowUnfinishedTasks == true) {
                var newList = [];
                for (var i = 0, len = oldList.length; i < len; i++) {
                    var t = oldList[i];
                    if (t.status != '2') newList.push(t);
                }
                return newList;
            } else {
                return oldList;
            }

        }

        function renderScheduleEvents(scheduleList) {
            var m1 = new Date();
            var calenderSpan = $('#calendar').fullCalendar('getView');
            var span = {
                start: moment(calenderSpan.start),
                end: moment(calenderSpan.end)
            }
            var allRenderingEvents = getDisplayingEvents(scheduleList, span);
            // for(var i = 0, len = scheduleList.length; i < len; i++){
            // 	scheduleList[i].isRecur = '0';
            // }

            var m2 = new Date();
            //allRenderingEvents = cleanRendered(allRenderingEvents);
            //console.debug(allRenderingEvents.length)
            $('#calendar').fullCalendarPlus('addCalEvent', allRenderingEvents.map(function(o) {
                if (typeof o.name == 'string') {
                    o.name = $.htmlEncode(o.name);
                }
                if (typeof o.title == 'string') {
                    o.title = $.htmlEncode(o.title);
                }
                return o;
            }));
            var m3 = new Date();
            //console.debug((m2-m1)+','+(m3-m2));
        };

        function getDisplayingEvents(scheduleList, span) {
            var allRenderingEvents = [];
            for (var i = 0, len = scheduleList.length; i < len; i++) {
                var data = scheduleList[i];
                data.startDateMom = $.isNumeric(data.startDate) ? moment(parseInt(data.startDate)) : moment(data.startDate);
                data.endDateMom = $.isNumeric(data.endDate) ? moment(parseInt(data.endDate)) : moment(data.endDate);
                data.finalFinishTime = $.isNumeric(data.finalFinishTime) ? moment(parseInt(data.finalFinishTime)) : moment(data.finalFinishTime);

                //if(data.id=='3703' || data.id=='3704'){
                //    data.endDateMom.add(24, 'hours');
                //}

                if (data.endDateMom.isBefore(data.startDateMom)) {
                    // if(window.console && console.warn) console.warn('ingage-web: end time can not before start time: id="'+data.id+'".');
                    continue;
                }
                if (data.endDateMom.isSame(data.startDateMom)) {
                    // if(window.console && console.warn) console.warn('ingage-web: start time and end time are equal: id="'+data.id+'"');
                    //continue;
                }
                var mi = getMomentInfo(span, data);
                if (data.isRecur == '1') {
                    var frequency = data.frequency; //DAY, WEEK, MONTH
                    if (!mi.interval) {
                        // if(window.console && console.warn) console.warn("ingage-web: repeating event's interval is null: id=\""+data.id+'".');
                        continue;
                    }
                    var events = [];
                    if (frequency === FRQ_BY_DAY) {
                        mi.type = 'days';
                        events = data.rules ? getRenderEventsByRules(mi, data) : getRenderEventsByDayWeekRepeat(mi, data);
                    } else
                    if (frequency === FRQ_BY_WEEK) {
                        mi.type = 'weeks';
                        events = data.rules ? getRenderEventsByRules(mi, data) : getRenderEventsByDayWeekRepeat(mi, data);
                    } else
                    if (frequency === FRQ_BY_MONTH) {
                        mi.type = 'months';
                        events = data.rules ? getRenderEventsByRules(mi, data) : getRenderEventsByMonthRepeat(mi, data);
                    }
                    allRenderingEvents = allRenderingEvents.concat(events);
                } else if (!data.isRecur || data.isRecur == '0') {
                    var calEvent = drawNormalEvent(mi, data);
                    allRenderingEvents.push(calEvent);
                }
            }
            return allRenderingEvents;
        };

        function renderTaskEvents(taskList) {
            $('#calendar').fullCalendarPlus('addTasks', taskList.map(function(o) {
                if (typeof o.taskName == 'string') {
                    o.taskName = $.htmlEncode(o.taskName);
                }
                return o;
            }));
        };

        function reRenderEvents() {
            scheduleUtil.showBusy();
            window.setTimeout(function() {
                $('#calendar').fullCalendar('removeEvents'); //clean old
                var schedulelist = filterSchedule(g_scheduleList);
                var tasklist = filterTask(g_taskList);
                renderScheduleEvents(schedulelist);
                renderTaskEvents(tasklist);
                $('#calendar').fullCalendarPlus('resetMyHeight');
                scheduleUtil.hideBusy();
            }, 20);

        };

        var current_ajax_counter = 0;

        function loadEvents(outerData) {
            if ($('#calendar').size() == 0 || !$('#calendar').is(':visible')) return;
            $('#calendar').fullCalendar('removeEvents');
            scheduleUtil.showBusy();
            var view = $('#calendar').fullCalendar('getView');
            if (typeof outerData != 'undefined') {
                _loadEvents(outerData);
                scheduleUtil.hideBusy();
                return;
            }
            var startDate = view.start.format('YYYY-MM-DD');
            var endDate = view.end.format('YYYY-MM-DD');
            var url = '/json/crm_schedule/schedule-list.action';

            var userId = scheduleUtil.getDisplaySetting('userId');
            var showTask = scheduleUtil.getDisplaySetting('showTask');
            var scheduleTypes = scheduleUtil.getDisplaySetting('scheduleTypes');
            var onlyShowUnfinishedTasks = scheduleUtil.getDisplaySetting('onlyShowUnfinishedTasks');

            var param = 'userId=' + userId +
                '&startDate=' + startDate +
                '&endDate=' + endDate +
                '&showTask=' + (showTask ? '1' : '0') +
                '&scheduleTypes=' + scheduleTypes +
                '&isFinish=' + (onlyShowUnfinishedTasks ? '0' : '1');
            current_ajax_counter++;
            $.ajax({
                    url: url,
                    data: param,
                    dataType: 'json'
                })
                .success(function(data) {
                    current_ajax_counter--;
                    if (current_ajax_counter != 0) return;
                    //console.debug(data)
                    _loadEvents(data);
                })
                .error(function() {})
                .done(function() {
                    $('#calendar').fullCalendarPlus('resetMyHeight');
                    scheduleUtil.hideBusy();
                });
        };

        function _loadEvents(data) {
            g_scheduleList = [];
            g_taskList = [];
            bindScheduleEvent(data);
            bindTaskEvent(data);
            updateUserInfos(data);
            scheduleUtil.currentData = data; //cache
            window.setTimeout(function() {
                $('#calendar').fullCalendarPlus('showTimeline');
            }, 500);
        };

        function updateUserInfos(data) {
            if (data && data.data && data.data.users)
                rk_userService.appendUserInfo(data.data.users);
        }


        //scheduleUtil
        //if(!window.scheduleUtil)window.scheduleUtil = {};
        scheduleUtil.bindScheduleEvent = bindScheduleEvent;
        scheduleUtil.renderScheduleEvents = renderScheduleEvents;
        scheduleUtil.renderTaskEvents = renderTaskEvents;
        scheduleUtil.loadEvents = loadEvents;
        scheduleUtil.reRenderEvents = reRenderEvents;

        //start!
        $(function() {

            //bindScheduleEvent(durationTestData2);
        })
    })();

    return scheduleUtil;
});
